package cc.rengu.igas.opms.core.service.trans.gateway;

import cc.rengu.igas.share.common.constant.ShareServiceConstant;
import cc.rengu.igas.share.common.constant.TransCacheConstant;
import cc.rengu.igas.share.common.enums.SettleAcctTypeEnum;
import cc.rengu.igas.opms.common.constant.OpmsServiceConstant;
import cc.rengu.igas.opms.common.constant.OpmsParamConstant;
import cc.rengu.igas.opms.common.enums.RespCodeEnum;
import cc.rengu.igas.opms.common.util.OpmsRSAUtils;
import cc.rengu.igas.opms.core.service.base.PayService;
import cc.rengu.igas.opms.facade.request.GatewayPayRequest;
import cc.rengu.igas.opms.facade.response.GatewayPayResponse;
import cc.rengu.igas.share.common.util.Base64;
import cc.rengu.igas.share.core.model.MchntInfo;
import cc.rengu.igas.share.core.model.UserInfo;
import cc.rengu.igas.share.core.realize.CsmcService;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.CsmcServiceImpl;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.jradp.asynnotify.AsynNotify;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.*;
import cc.rengu.oltp.service.common.dao.impl.*;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.realize.OrderService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.OrderServiceImpl;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.base.Base64Util;
import com.alibaba.fastjson.JSON;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 网关支付
 *
 * @author keyi
 * @modify by wangcheng
 * @date 2020/4/23.
 */
public class GatewayPayService extends PayService {

    @Override
    protected Object bizBeforeCust() throws Exception {
        GatewayPayRequest gatewayPayRequest = new GatewayPayRequest();
        ConvertUtil.convertOutput(gatewayPayRequest);
        return gatewayPayRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        GatewayPayRequest gatewayPayRequest = (GatewayPayRequest) request;
        String instId = gatewayPayRequest.getHeader().getInstId();
        String sessionId = gatewayPayRequest.getHeader().getSession();
        String txnNum = gatewayPayRequest.getHeader().getTxnNum();
        String bizType = gatewayPayRequest.getHeader().getBizType();
        String userId = gatewayPayRequest.getUserId();
        String userOrderId = gatewayPayRequest.getUserOrderId();

        //获取订单信息
        OrderService orderService = new OrderServiceImpl();
        OrderInfo orderInfo = orderService.selectOrderInfoByUserOrderId(instId, userOrderId);
        if (null == orderInfo) {
            rglog.error("订单信息不存在!");
            throw new BizException(RespCodeEnum.ORDER_NOEXIT.getRespCode(), RespCodeEnum.ORDER_NOEXIT.getRespDesc());
        }
        UserOrderInfo userOrderInfo = orderInfo.getUserOrderInfo();
        //更新用户订单中的订单备注信息
        userOrderInfo.setOrderDesc(gatewayPayRequest.getOrderDesc());
        userOrderInfo.setSysSeqNum(sysSeqNum);
        List<MchntOrderInfo> mchntOrderInfoList = orderInfo.getMchntOrderInfoList();
        List<MchntOrderDetail> mchntOrderDetailList = orderInfo.getMchntOrderDetailList();
        if (null != mchntOrderDetailList && !mchntOrderDetailList.isEmpty()) {
            mchntOrderDetailList.forEach(item -> item.setSysSeqNum(sysSeqNum));
        }
        String mchntName = userOrderInfo.getGroupMchntName();

        //购买卡券不支持使用优惠券
        if ("00".equals(mchntOrderInfoList.get(0).getBizSceneType()) || "02".equals(mchntOrderInfoList.get(0).getBizSceneType())) {
            GlobalTransUtil.setCacheValue(TransCacheConstant.BUY_COUPON_TRANS_FLAG, AppParamConstant.YES);
            if (gatewayPayRequest.getCouponInfo() != null && gatewayPayRequest.getCouponInfo().getDiscountId() != null) {
                rglog.error("购买卡券交易不支持使用卡券");
                throw new BizException(RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespCode(), RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespDesc());
            }
        }

        //合并订单支付，不能合并特殊费率跟非特殊费率，不能合并d0清算跟非d0清算
        if (mchntOrderInfoList.size() > 1) {
            List<MchntOrderInfo> mchntOrderInfoSpFeeList = mchntOrderInfoList.stream().filter(item -> "0".equals(item.getFeeInputMode())).collect(Collectors.toList());
            if (!mchntOrderInfoSpFeeList.isEmpty() && mchntOrderInfoSpFeeList.size() != mchntOrderInfoList.size()) {
                rglog.error("合并订单中存在特殊费率跟非特殊费率的订单，拒绝交易");
                updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
                throw new BizException(RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespCode(), RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespDesc());
            }
            List<MchntOrderInfo> mchntOrderInfoD0List = mchntOrderInfoList.stream().filter(item -> AppParamConstant.YES.equals(item.getRealSettleFlag())).collect(Collectors.toList());
            if (!mchntOrderInfoD0List.isEmpty() && mchntOrderInfoD0List.size() != mchntOrderInfoList.size()) {
                rglog.error("合并订单中存在d0清算跟非d0清算的订单，拒绝交易");
                updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
                throw new BizException(RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespCode(), RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespDesc());
            }
        } else {
            mchntName = mchntOrderInfoList.get(0).getMchntName();
        }

        //商户权限及限额检查
        MchntService mchntService = new MchntServiceImpl();
        for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
            /*获取商户基本信息表*/
            MchntInfo mchntInfo = mchntService.getMchntInfo(instId, mchntOrderInfo.getMchntNo());
            /*判断商户是否是独立清算的门店商户*/
            if ("11".equals(mchntInfo.getMchntType()) && AppParamConstant.NO.equals(mchntInfo.getIndependentDataFlag())) {
                rglog.debug("非独立维护资料的门店商户商户订单表记录其连锁商户号");
                mchntOrderInfo.setChainMchntNo(mchntInfo.getChainMchntNo());
                mchntInfo = mchntService.getMchntInfo(instId, mchntInfo.getChainMchntNo());
            }
            /* 商户交易权限检查 */
            boolean checkResult = mchntService.checkMchntAuth(instId, mchntOrderInfo.getMchntNo(), null, txnNum, bizType, AcctTypeEnum.BANK_CREDIT.getAcctType());
            if (!checkResult) {
                rglog.error("商户<{}>无此交易权限!", mchntOrderInfo.getMchntNo());
                throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
            }
            /* 商户限额校验 */
            int limitFlag = mchntService.checkMchntTransLimit(instId, mchntOrderInfo.getRealSettleAmt(), LimitAcctTypeEnum.ALL, mchntOrderInfo.getMchntNo(), null, txnNum);
            if (limitFlag == 2 || limitFlag == 3) {
                rglog.error("商户金额超限");
                throw new BizException(RespCodeEnum.MCHNT_AMT_OVER.getRespCode(), RespCodeEnum.MCHNT_AMT_OVER.getRespDesc());
            } else if (limitFlag == 1) {
                rglog.debug("商户贷记卡超限，交易上送不支持贷记卡标识");
                xmlTreeUtil.setXmlTreeStringValue("limitPay", "no_credit");
            }
            //网关支付成功后计算手续费
            mchntOrderInfo.setSysSeqNum(sysSeqNum);
            mchntOrderInfo.setBizType(bizType);
            mchntOrderInfo.setTxnNum(txnNum);
            mchntOrderInfo.setMchntDiscountFee("0.00");
            mchntOrderInfo.setMchntSettleAmt(mchntOrderInfo.getRealSettleAmt());
            mchntOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
            mchntOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
            mchntOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            mchntOrderInfo.setBizProdCode(txnInfo.getSrcTxnAuthCfg().getBizProdCode());
            //网关支付且使用优惠劵的才判断是否实时清算
            if (gatewayPayRequest.getCouponInfo() != null && gatewayPayRequest.getCouponInfo().getDiscountId() != null) {
                mchntOrderInfo.setRealSettleFlag(AppParamConstant.NO);
            } else {
                String realSettleFlag = mchntService.getRealSettleFlag(mchntInfo.getMchntNo(), txnInfo.getSrcTxnAuthCfg().getBizProdCode(), mchntOrderInfo.getBizSceneType(), mchntOrderInfo.getRealSettleAmt());
                mchntOrderInfo.setRealSettleFlag(realSettleFlag);
                if (AppParamConstant.YES.equals(mchntOrderInfo.getRealSettleFlag())) {
                    mchntOrderInfo.setRealSettleType("99");
                }
            }
        }

        //更新订单信息
        if (!orderService.updateOrderInfoByOrderId(userOrderInfo, mchntOrderInfoList, mchntOrderDetailList)) {
            rglog.error("更新订单信息失败");
            throw new BizException(RespCodeEnum.UPDATE_ORDER_ERROR.getRespCode(), RespCodeEnum.UPDATE_ORDER_ERROR.getRespDesc());
        }

        //生成网关支付报文
        String orderTime = userOrderInfo.getTxnDate() + userOrderInfo.getTxnTime();
        GatewayPayResponse gatewayPayResponse;
        if ("00".equals(bizType)) {
            //本行个人网关支付
            CsmcService csmcService = new CsmcServiceImpl();
            String roleActualAcctNo = csmcService.getRoleActualAcctNo(instId, SettleAcctTypeEnum.BANK_PAYCHNL_SUBJECT.getSettleAcct(), null);
            if (StringUtil.isEmptyOrNull(roleActualAcctNo)) {
                updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
                rglog.error("当前机构未配置渠道户,交易终止");
                throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
            }
            gatewayPayResponse = generatePersonBankGatePayMsg(gatewayPayRequest, sysSeqNum, orderTime, roleActualAcctNo, mchntName);
        } else if ("01".equals(bizType)) {
            //本行企业网关支付
            gatewayPayResponse = generateCompanyBankGatePayMsg(gatewayPayRequest, mchntName, sysSeqNum);
        } else if ("04".equals(bizType)) {
            //中金网关支付
            gatewayPayResponse = generateCpcnGatewayPayMsg(instId, sysSeqNum, userOrderInfo);
        } else {
            rglog.error("暂不支持的交易细分:<{}>", bizType);
            throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
        }

        //生成支付订单信息
        PayOrderInfo payOrderInfo = generatePayOrderInfo(gatewayPayRequest, userOrderInfo);
        if (!orderService.registerOrderInfo(null, null, payOrderInfo)) {
            rglog.error("登记支付订单表失败,payOrderInfo=[{}]", JSON.toJSONString(payOrderInfo));
            return null;
        } else {
            rglog.debug("登记支付订单表成功,payOrderInfo:<{}>", JSON.toJSONString(payOrderInfo));
        }

        /*异步调用订单查证接口*/
        AsynNotify asynNotify = new AsynNotify();
        int iReturnCode = asynNotify.transactionNotice(ShareServiceConstant.ASYN_PAYORDERSTATUS_QUERY_SERVICE, 5, AsynNotify.GAP_SAME, 60 * 1000);
        if (iReturnCode != 0) {
            rglog.error("异步调起全渠道交易状态查询失败!");
        }

        // 组装应答报文
        BizResponse<GatewayPayResponse> bizResponse = new BizResponse<>();
        bizResponse.setRspSysId(AppParamConstant.SYS_ID);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        bizResponse.setResult(gatewayPayResponse);
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        GatewayPayResponse gatewayPayResponse = (GatewayPayResponse) bizResponse.getResult();
        ConvertUtil.convertInput(gatewayPayResponse);
    }

    /**
     * 组装中金网关请求报文
     *
     * @param instId        多法人标识
     * @param sysSeqNum     系统流水号
     * @param userOrderInfo 用户订单信息
     * @return 响应信息
     * @throws Exception 异常
     */
    private GatewayPayResponse generateCpcnGatewayPayMsg(String instId, String sysSeqNum, UserOrderInfo userOrderInfo) throws Exception {
        GatewayPayResponse gatewayPayResponse = new GatewayPayResponse();
        /*获取支付结果回调地址*/
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_RETURN_URL, ChannelEnum.CPCN.getChannelType());
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("支付结果回调地址未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String returnUrl = sysParam.getParamValue().trim();
        rglog.info("支付结果回调地址<{}>", returnUrl);

        //获取网关回调地址
        sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.GATEWAY_RETURN_URL, ChannelEnum.CPCN.getChannelType());
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("中金网关跳转URL未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String gateWayReturnUrl = sysParam.getParamValue().trim();
        rglog.info("中金网关跳转URL<{}>", gateWayReturnUrl);

        /*查询中金机构号*/
        sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_SYS_PARAM, OpmsParamConstant.INSTITUTIONID);
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("商户<{}>的中金机构号未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        //拼装中金网关支付报文
        String institutionID = sysParam.getParamValue().trim();
        long orderAmt = Long.parseLong(AmountUtil.multiplication(userOrderInfo.getRealPayAmt(), "100", 0));
        rglog.info("支付金额为<{}>", orderAmt);
        StringBuilder stringBuffer = new StringBuilder();
        stringBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
        String temp = "<Request version=\"2.1\">" +
                "<Head>" +
                "<TxCode>" + "1112" + "</TxCode>" +
                "</Head>" +
                "<Body>" +
                /*机构号*/
                "<InstitutionID>" + institutionID + "</InstitutionID>" +
                /*支付流水号*/
                "<PaymentNo>" + sysSeqNum + "</PaymentNo>" +
                /*金额*/
                "<Amount>" + orderAmt + "</Amount>" +
                /*结算标识*/
                "<SettlementFlag>" + "0002" + "</SettlementFlag>" +
                /*订单描述*/
                "<Remark>" + "中金" + "</Remark>" +
                /*用户自定义信息*/
                "<Note>" + "22545" + "</Note>" +
                /*异步通知地址*/
                "<NotificationURL>" + returnUrl + "</NotificationURL>" +
                "</Body>" +
                "</Request>";
        stringBuffer.append(temp);
        rglog.info("中金网关支付请求报文<{}>", stringBuffer.toString());
        byte[] data = stringBuffer.toString().getBytes(StandardCharsets.UTF_8);
        Base64Util base64Util = new Base64Util();
        String gateWayReqMsg = base64Util.encode(data);

        //获取通道信息
        DstChannelInfoMapper dstChannelInfoMapper = new DstChannelInfoMapperImpl();
        DstChannelInfo dstChannelInfo = dstChannelInfoMapper.selectDstChannelInfoByPrimaryKey(instId, ChannelEnum.CPCN.getChannelType());
        if (null == dstChannelInfo) {
            rglog.error("获取通道<{}>信息失败", ChannelEnum.CPCN.getChannelType());
            throw new BizException(RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespCode(), RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespDesc());
        }
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        SecPlanInfo secPlanInfo = secPlanInfoMapper.selectSecPlanInfoByPrimaryKey(instId, dstChannelInfo.getSecPlanId(), "CE10");
        if (null == secPlanInfo) {
            rglog.error("安全计划信息为配置secPlanId:<{}>,secPlanType:<{}>", dstChannelInfo.getSecPlanId(), "CE10");
            throw new BizException(RespCodeEnum.CERT_LOAD_INVALID.getRespCode(), RespCodeEnum.CERT_LOAD_INVALID.getRespDesc());
        }
        //获取证书路径
        CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
        CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + dstChannelInfo.getSecPlanId() + "CE10");
        if (null == certInfo) {
            throw new BizException(RespCodeEnum.CERT_LOAD_INVALID.getRespCode(), RespCodeEnum.CERT_LOAD_INVALID.getRespDesc());
        }
        //证书密钥串
        String signData = null;
        if ("0".equals(certInfo.getCertSaveType())) {
            RSAPrivateKey privateKey = RSAUtil.getPrivateKeyFromBase64String(certInfo.getCertValue());
            signData = generateSignString(gateWayReqMsg.getBytes(StandardCharsets.UTF_8), privateKey);
        } else if ("2".equals(certInfo.getCertSaveType())) {
            List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
            RSAPrivateKey privateKey = RSAUtil.getPrivateKeyFromPfxCertFile(certInfo.getCertSavePath(), aliasList.get(0), certInfo.getCertPassword());
            signData = generateSignString(gateWayReqMsg.getBytes(StandardCharsets.UTF_8), privateKey);
        }
        gatewayPayResponse.setGatewayReqMsg(gateWayReqMsg);
        gatewayPayResponse.setGatewayReqSignMsg(signData);
        return gatewayPayResponse;
    }

    /**
     * 本行个人网关组装响应
     *
     * @param request   请求体
     * @param orderTime 订单时间
     * @return 响应信息
     * @throws Exception 异常
     */
    private GatewayPayResponse generatePersonBankGatePayMsg(GatewayPayRequest request, String orderTime, String sysSeqNum, String roleActualAcctNo, String mchntName) throws Exception {
        GatewayPayResponse gatewayPayResponse = new GatewayPayResponse();
        String instId = request.getHeader().getInstId();
        /*获取支付结果回调地址*/
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_RETURN_URL, "GW01");
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("支付结果回调地址未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String returnUrl = sysParam.getParamValue();
        /*查询本行个人网关URL*/
        sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.GATEWAY_RETURN_URL, "GW01");
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("本行企业网关跳转URL未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String b2cReturnUrl = sysParam.getParamValue();
        DstChannelCfgMapper dstChannelCfgMapper = new DstChannelCfgMapperImpl();
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
        //获取安全计划ID
        DstChannelCfg dstChannelCfg = dstChannelCfgMapper.selectDstChannelCfgByPrimaryKey(instId, "GATEWAY", "");
        if (null == dstChannelCfg || StringUtil.isEmptyOrNull(dstChannelCfg.getSecPlanId())) {
            rglog.error("支付通道信息表未配置安全计划ID");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        //获取安全计划信息
        List<SecPlanInfo> secPlanInfos = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        SecPlanInfo upacSecPlanInfo = null;
        for (SecPlanInfo secPlanInfo : secPlanInfos) {
            if (secPlanInfo.getSecPlanType().startsWith("CE")) {
                upacSecPlanInfo = secPlanInfo;
            }
        }
        if (null == upacSecPlanInfo || AppParamConstant.NO.equals(upacSecPlanInfo.getSecPlanStatus())) {
            rglog.error("安全计划信息表未配置全渠道数据信息或已失效");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }

        //通过证书获取私钥
        String certIndex = instId + upacSecPlanInfo.getSecPlanId() + upacSecPlanInfo.getSecPlanType();
        CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(certIndex);
        if (null == certInfo || AppParamConstant.NO.equals(certInfo.getKeyStatus())) {
            rglog.error("证书信息表中未配置全渠道加密证书或证书已失效");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        RSAPrivateKey privateKey = null;
        if (OpmsParamConstant.STR_TWO.equals(certInfo.getCertSaveType())) {
            rglog.info("证书以文件的方式存放，certIndex:<{}>,certSavePath:<{}>", certIndex, certInfo.getCertSavePath());
            try {
                List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
                privateKey = RSAUtil.getPrivateKeyFromPfxCertFile(certInfo.getCertSavePath(), aliasList.get(0), certInfo.getCertPassword());
            } catch (IOException e) {
                rglog.error("读取证书文件异常:{}", e.getMessage());
                throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
            }
        } else {
            rglog.error("获取个人网关支付加密证书失败!");
        }

        //获取加签数据并加签
        HashMap<String, Object> dataMap = new HashMap<>();
        dataMap.put("BANKID", instId);
        dataMap.put("CHANNALID", "1103");
        dataMap.put("MERCHANTID", "80011030001");
        dataMap.put("MERCHANTNAME", mchntName);
        dataMap.put("ORDERID", sysSeqNum);
        dataMap.put("TRANTIME", orderTime);
        dataMap.put("PAYMENT", request.getOrderAmt());
        dataMap.put("ORDERAMOUNT", "1");
        dataMap.put("REACCNO", roleActualAcctNo);
        dataMap.put("REACCNAME", "");
        dataMap.put("PAYACCNO", "");
        dataMap.put("CURCODE", "01");
        dataMap.put("MERCHANTURL", returnUrl);
        dataMap.put("FIRSTMERNO", request.getPlatMchntNo());
        dataMap.put("SECONDMERNO", "");
        dataMap.put("ORIGINNO", request.getUserOrderId());
        dataMap.put("BIZFLAG", "1");
        String opReqStr = createXmlByMap(dataMap, "opReq", true);
        /*数据加密*/
        String signData = RSAUtil.generateSignString(opReqStr, privateKey, "SHA1WithRSA", OpmsParamConstant.UTF_8_ENCODING);
        HashMap<String, Object> signedDataMap = new HashMap<>();
        signedDataMap.put("SignedData", signData);
        String signStr = createXmlByMap(signedDataMap, "Sign", true);
        // 合并请求报文
        StringBuilder reqMsg = new StringBuilder("<?xml version='1.0' encoding='UTF-8'?><BankData>");
        reqMsg.append(signStr).append(opReqStr).append("</BankData>");
        rglog.info("本行个人网关跳转报文<{}>", reqMsg.toString());
        String gateWayReqMsg = new String(Base64.encode(reqMsg.toString().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        gatewayPayResponse.setGatewayReqMsg(gateWayReqMsg);
        gatewayPayResponse.setGatewayUrl(b2cReturnUrl);
        return gatewayPayResponse;
    }

    /**
     * 本行企业网关获取应答报文
     *
     * @param request   请求体
     * @param mchntName 商户名称
     * @return 响应实体
     * @throws Exception 异常
     */
    private GatewayPayResponse generateCompanyBankGatePayMsg(GatewayPayRequest request, String mchntName, String sysSeqNum) throws Exception {
        GatewayPayResponse gatewayPayResponse = new GatewayPayResponse();
        SysParamService sysParamService = new SysParamServiceImpl();
        /*获取支付结果回调地址*/
        SysParam sysParam = sysParamService.getSysParamInfo(request.getHeader().getInstId(), OpmsParamConstant.OPMS_RETURN_URL, "BB01");
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("支付结果回调地址未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String returnUrl = sysParam.getParamValue();
        /*查询本行企业网关URL*/
        sysParam = sysParamService.getSysParamInfo(request.getHeader().getInstId(), OpmsParamConstant.GATEWAY_RETURN_URL, "BB01");
        if (null == sysParam || StringUtil.isEmptyOrNull(sysParam.getParamValue())) {
            rglog.error("本行企业网关跳转URL未配置");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String b2bReturnUrl = sysParam.getParamValue();
        //获取请求报文
        HashMap<String, Object> dataMsg = new HashMap<>();
        dataMsg.put("CHANNALNUM", "2005");
        dataMsg.put("TRANTYPE", "0");
        dataMsg.put("MERCHANTID", "80020050001");
        dataMsg.put("MERCHANTNAME", mchntName);
        dataMsg.put("PAYMENT", request.getOrderAmt());
        dataMsg.put("ORDERID", sysSeqNum);
        dataMsg.put("TRANDATE", DateUtil.getCurrentDateTime("YYYY-MM-dd") + "T" + DateUtil.getCurrentDateTime("HH:mm:ss"));
        dataMsg.put("MERCHANTURL", returnUrl);
        dataMsg.put("CURCODE", "01");
        dataMsg.put("REMARK", "");
        dataMsg.put("FIRSTMERNO", request.getPlatMchntNo());
        dataMsg.put("SECONDMERNO", "");
        dataMsg.put("ORIGINNO", request.getUserOrderId());

        String dataMsgStr = createXmlByMap(dataMsg, "opReq", true);
        StringBuilder reqMsg = new StringBuilder("<?xml version='1.0' encoding='UTF-8'?>\r\n<BankData xmlns='http://www.bylife.com/schema/PYGatewayPackage'> \r\n");
        reqMsg.append(dataMsgStr).append("</BankData>");
        rglog.info("本行企业网关支付报文<{}>", reqMsg.toString());
        //BASE64加密
        Base64Util base64Util = new Base64Util();
        String gatewayReqMsg = OpmsRSAUtils.encode(base64Util.encode(reqMsg.toString().getBytes()));

        gatewayPayResponse.setGatewayReqMsg(gatewayReqMsg);
        gatewayPayResponse.setGatewayUrl(b2bReturnUrl);
        return gatewayPayResponse;
    }

    /**
     * 将Map转换为XML,Map可以多层转
     *
     * @param map        需要转换的map。
     * @param parentName 就是map的根key,如果map没有根key,就输入转换后的xml根节点。
     * @return String-->XML
     */
    public String createXmlByMap(Map<String, Object> map, String parentName, Boolean suppressDeclaration) {
        //获取map的key对应的value
        Map<String, Object> rootMap = (Map<String, Object>) map.get(parentName);
        if (rootMap == null) {
            rootMap = map;
        }
        Document doc = DocumentHelper.createDocument();
        //设置根节点
        doc.addElement(parentName);
        String xml = iteratorXml(doc.getRootElement(), parentName, rootMap);
        return formatXML(xml, suppressDeclaration);
    }

    /**
     * 循环遍历params创建xml节点
     *
     * @param element    根节点
     * @param parentName 子节点名字
     * @param params     map数据
     * @return String-->Xml
     */
    public static String iteratorXml(org.dom4j.Element element, String parentName, Map<String, Object> params) {
        org.dom4j.Element e = element.addElement(parentName);
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (entry.getValue() instanceof Map) {
                iteratorXml(e, entry.getKey(), (Map<String, Object>) entry.getValue());
            } else if (entry.getValue() instanceof List) {
                List<Object> list = (List<Object>) entry.getValue();
                for (Object o : list) {
                    iteratorXml(e, entry.getKey(), (Map<String, Object>) o);
                }
            } else {
                String value = entry.getValue() == null ? "" : entry.getValue().toString();
                e.addElement(entry.getKey()).addText(value);
            }
        }
        return e.asXML();
    }

    /**
     * 格式化xml文档
     *
     * @param xml                 内容
     * @param suppressDeclaration 格式化标志
     * @return 格式化的字符串
     */
    public String formatXML(String xml, Boolean suppressDeclaration) {
        String requestXML = null;
        XMLWriter writer = null;
        Document document;
        try {
            SAXReader reader = new SAXReader();
            document = reader.read(new StringReader(xml));
            if (document != null) {
                StringWriter stringWriter = new StringWriter();
                OutputFormat format = new OutputFormat("", true);
                format.setNewLineAfterDeclaration(false);
                format.setSuppressDeclaration(suppressDeclaration);
                format.setNewlines(false);
                writer = new XMLWriter(stringWriter, format);
                writer.write(document);
                writer.flush();
                requestXML = stringWriter.getBuffer().toString();
            }
            return requestXML;
        } catch (Exception e) {
            rglog.info("格式转换失败:{}", e);
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    rglog.info("关闭流失败", e);
                    throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
                }
            }
        }
    }

    private static String generateSignString(byte[] signBlock, RSAPrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance("SHA1withRSA");
        signature.initSign(privateKey);
        signature.update(signBlock);
        return StringUtil.binaryToHexString(signature.sign());
    }

    /**
     * 生成支付订单信息
     *
     * @param gatewayPayRequest 网关支付请求信息
     * @param userOrderInfo     用户订单信息
     * @return 支付订单信息
     * @throws Exception 异常
     */
    private PayOrderInfo generatePayOrderInfo(GatewayPayRequest gatewayPayRequest, UserOrderInfo userOrderInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        rglog.error("用户订单信息为<{}>", JSON.toJSONString(userOrderInfo));
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        String bizType = gatewayPayRequest.getHeader().getBizType();
        PayOrderInfo payOrderInfo = new PayOrderInfo();
        BeanUtil.beanCopy(userOrderInfo, payOrderInfo);
        switch (bizType) {
            case "00":
            case "01":
                //本行个人或企业网关支付
                payOrderInfo.setMsgDstId(ChannelEnum.BKGW.getChannelType());
                payOrderInfo.setPayType(PayTypeEnum.BANK_CARD.getPayType());
                payOrderInfo.setDstChannelType("00".equals(bizType) ? "GW01" : "BB01");
                break;
            case "02":
            case "03":
                //预留的银联全渠道网关支付，产品目前暂不支持该种方式
                break;
            case "04":
                //中金网关支付
                payOrderInfo.setMsgDstId(ChannelEnum.CPCN.getChannelType());
                payOrderInfo.setPayType(PayTypeEnum.OTHER_BANK_CARD.getPayType());
                break;
            default:
                break;
        }
        payOrderInfo.setTxnDate(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE));
        payOrderInfo.setTxnTime(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME));
        payOrderInfo.setTxnNum(gatewayPayRequest.getHeader().getTxnNum());
        payOrderInfo.setBizType(gatewayPayRequest.getHeader().getBizType());
        payOrderInfo.setRealPayAmt(userOrderInfo.getRealPayAmt());
        payOrderInfo.setPayerAcctType("");
        payOrderInfo.setPayeeAcctType("");
        payOrderInfo.setPayeeAcctInfo("");
        payOrderInfo.setSysSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
        payOrderInfo.setDstCallIndex(OpmsParamConstant.STR_ZERO);
        payOrderInfo.setDstTxnNum("");
        payOrderInfo.setChannelSeqNum("");
        payOrderInfo.setChannelReconKey(sysSeqNum);
        payOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        payOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        payOrderInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        return payOrderInfo;
    }

    /**
     * 支付失败后/异常统一更新订单状态为支付失败
     *
     * @param userOrderInfo      用户订单信息
     * @param mchntOrderInfoList 商户订单信息
     * @throws Exception 异常
     */
    private void updateOrderInfoFail(UserOrderInfo userOrderInfo, List<MchntOrderInfo> mchntOrderInfoList) throws Exception {
        OrderService orderService = new OrderServiceImpl();
        /* Step1：更新用户订单信息 */
        if (null != userOrderInfo) {
            userOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
            userOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
        }
        /* Step2：更新商户订单信息 */
        if (null != mchntOrderInfoList && !mchntOrderInfoList.isEmpty()) {
            for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
                mchntOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
                mchntOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
            }
        }
        /* Step3：更新订单信息 */
        if (!orderService.updateOrderInfo(userOrderInfo, mchntOrderInfoList)) {
            rglog.error("更新订单表状态失败");
            /*返回结果未知，让前端发起查询*/
            throw new BizException(RespCodeEnum.BIZ_TIMEOUT_ERROR.getRespCode(), RespCodeEnum.BIZ_TIMEOUT_ERROR.getRespDesc());
        }
    }
}
